/**
 * xnikt - XSD refactoring tool
 * Copyright (C) 2008 Dharivs from Byte Fo-Ji (http://bytefo-ji.blogspot.com)
 * 
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */
package com.bytefoji.xnikt;

import com.ximpleware.AutoPilot;
import com.ximpleware.ModifyException;
import com.ximpleware.NavException;
import com.ximpleware.PilotException;
import com.ximpleware.TranscodeException;
import com.ximpleware.VTDGen;
import com.ximpleware.VTDNav;
import com.ximpleware.XMLModifier;
import com.ximpleware.XPathEvalException;
import com.ximpleware.XPathParseException;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import javolution.text.TextBuilder;
import javolution.util.FastSet;
import javolution.util.FastTable;

/**
 *
 * @author dharivs
 */
public class XniktApp {

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) throws PilotException, NavException, ModifyException, FileNotFoundException, IOException, TranscodeException, XPathParseException, XPathEvalException {
        if (args.length < 2) {
            XniktApp.usage();
        }

        XniktApp xa = new XniktApp();

        xa.doRefactor(args);
    }

    private void doRefactor(String[] args) throws TranscodeException, FileNotFoundException, XPathParseException, ModifyException, IOException, XPathEvalException, NavException, IOException {
        /**
         * Table with sets of xsd elements which are in the first depth level
         * (under the root).
         * 
         * One set per input file.
         */
        FastTable<FastSet<XSDElement>> element_sets = this.extractElementsPerFile(args);

        /**
         * It intersects all the sets to get the common elements.
         */
        FastSet<XSDElement> result = this.intersectElementsSets(element_sets);

        /**
         * It removes all common elements from every input file.
         */
        this.removeCommonElementsInInputFiles(args, result);

        /**
         * And it generates the common.xsd file with all common elements.
         */
        this.generateCommonXSD(result);
    }

    private FastTable<FastSet<XSDElement>> extractElementsPerFile(String[] args) throws NavException {
        FastTable<FastSet<XSDElement>> element_sets = new FastTable<FastSet<XSDElement>>();

        VTDGen vg = new VTDGen();
        for (int i = 0; i < args.length; i++) {
            if (vg.parseFile(args[i], true)) {
                VTDNav vn = vg.getNav();
                AutoPilot ap = new AutoPilot(vn);
                ap.selectElementNS("http://www.w3.org/2001/XMLSchema", "*");
                vn.toElement(VTDNav.FIRST_CHILD); // We skip the root.

                int current_idx = 0;
                FastSet<XSDElement> fs = new FastSet<XSDElement>();

                /**
                 * It iterates through the select elements with ap.selectElementNS,
                 * checking for tags belonging to element, simpleType, complexType
                 * or attributeGroup types.
                 */
                while (ap.iterate()) {
                    current_idx = vn.getCurrentIndex();

                    if (vn.getCurrentDepth() == 1) {
                        String tag = vn.toString(current_idx);

                        if (tag.equals("xs:element") || tag.equals("xs:simpleType") || tag.equals("xs:complexType") || tag.equals("xs:attributeGroup")) {
                            String name = vn.toString(vn.getAttrVal("name"));

                            XSDElement current_element = new XSDElement(name, current_idx, tag);
                            fs.add(current_element);
                        }
                    }
                }

                element_sets.add(fs);
            }

            vg.clear();
        }

        return element_sets;
    }

    private void generateCommonXSD(FastSet<XSDElement> result) throws FileNotFoundException, IOException {

        String root = "<xs:schema targetNamespace=\"http://your_namespace\" xmlns:xs=\"http://www.w3.org/2001/XMLSchema\" xmlns=\"http://your_schema\" elementFormDefault=\"qualified\">";
        FileOutputStream fos = new FileOutputStream("common.xsd");
        fos.write(root.getBytes());
        fos.write("\n".getBytes());

        for (FastSet.Record r = result.head(), end = result.tail(); (r = r.getNext()) != end;) {
            XSDElement e = result.valueOf(r);

            fos.write(e.getXML().getBytes());
            fos.write("\n".getBytes());
        }

        fos.write("</xs:schema>".getBytes());
        fos.write("\n".getBytes());
    }

    private FastSet<XSDElement> intersectElementsSets(FastTable<FastSet<XSDElement>> element_sets) {

        FastSet<XSDElement> result = new FastSet<XSDElement>();
        result.addAll(element_sets.get(0));

        for (int i = 1, n = element_sets.size(); i < n; i++) {
            result.retainAll(element_sets.get(i));
        }

        return result;
    }

    private void removeCommonElementsInInputFiles(String[] args, FastSet<XSDElement> result) throws TranscodeException, XPathEvalException, ModifyException, XPathParseException, IOException, NavException {

        VTDGen vg = new VTDGen();
        for (int i = 0, n = args.length; i < n; i++) {
            if (vg.parseFile(args[i], true)) {
                String[] path = args[i].split("/");
                String output = path[path.length - 1];

                VTDNav vn = vg.getNav();
                AutoPilot ap = new AutoPilot(vn);
                XMLModifier xm = new XMLModifier(vn);

                ap.declareXPathNameSpace("xs", "http://www.w3.org/2001/XMLSchema");

                for (FastSet.Record r = result.head(), end = result.tail(); (r = r.getNext()) != end;) {
                    XSDElement element = result.valueOf(r);
                    this.seekAndRemove(element, ap, vn, xm);
                }

                xm.output(output);
                xm.reset();
            }
        }
    }

    private String extractElementXML(VTDNav vn) throws NavException {
        long l = vn.getElementFragment();
        int offset = (int) l;
        int length = (int) (l >> 32);

        return new String(vn.getXML().getBytes(offset, length));
    }

    private void seekAndRemove(XSDElement e, AutoPilot ap, VTDNav vn, XMLModifier xm) throws XPathParseException, XPathEvalException, ModifyException, NavException {
        TextBuilder tb = TextBuilder.newInstance();

        tb.append("/xs:schema/");
        tb.append(e.getTag());
        tb.append("[@name='");
        tb.append(e.getName());
        tb.append("']");

        ap.selectXPath(tb.toString());

        int j = -1;
        while ((j = ap.evalXPath()) != -1) {
            e.setXML(this.extractElementXML(vn));
            xm.remove();
        }

        ap.resetXPath();
    }

    private static void usage() {
        TextBuilder tb = TextBuilder.newInstance();

        tb.append("Usage: xnikt [xsd-files]+\n");
        tb.append("\nxnikt revision 001, Copyright (C) 2008 Dharivs from Byte Fo-Ji (http://bytefo-ji.blogspot.com)\n");
        tb.append("xnikt comes with ABSOLUTELY NO WARRANTY; This is free software, and you are welcome");
        tb.append("to redistribute it under certain conditions.");

        System.out.println(tb.toString());

        System.exit(1);
    }
}
